package loader

import (
	"context"
	"fmt"
	"go-caipu/pkg/plugins"
	"go-caipu/pkg/plugins/log"
	"go-caipu/pkg/plugins/manager/loader/finder"
	"go-caipu/pkg/plugins/manager/loader/initializer"
	"go-caipu/pkg/plugins/manager/process"
	"go-caipu/pkg/plugins/manager/registry"
	"go-caipu/pkg/plugins/storage"
	"go-caipu/pkg/setting"
)

type Loader struct {
	pluginFinder      finder.Finder
	processManager    process.Service
	pluginRegistry    registry.Service
	pluginInitializer initializer.Initializer
	//signatureValidator
	//signatureCalculator
	//pluginStorge storage.Manager
	pluginStorage storage.Manager
	log           log.Logger
	cfg           *setting.Cfg
	errs          map[string]*plugins.SignatureError
}

func ProvideService(cfg *setting.Cfg, pluginRegistry registry.Service, pluginFinder finder.Finder, processManager process.Service, backendProvider plugins.BackendFactoryProvider) *Loader {
	return &Loader{
		log:               log.New("plugins.loader"),
		cfg:               cfg,
		errs:              make(map[string]*plugins.SignatureError),
		pluginRegistry:    pluginRegistry,
		pluginInitializer: initializer.New(cfg, backendProvider),
		//pluginStorage:  storage.NewManager(log.New("loader.storage"), cfg.Application.PluginsPath),
		pluginFinder:   pluginFinder,
		processManager: process.NewManager(pluginRegistry),
		pluginStorage:  storage.FileSystem(log.NewPrettyLogger("loader.fs"), cfg.Application.PluginsPath),
	}
}
func (l *Loader) Load(ctx context.Context, src plugins.PluginSource) ([]*plugins.Plugin, error) {
	found, err := l.pluginFinder.Find(ctx, src)
	if err != nil {
		return nil, err
	}
	return l.loadPlugins(ctx, src, found)
}
func (l *Loader) loadPlugins(ctx context.Context, src plugins.PluginSource, found []*plugins.FoundBundle) ([]*plugins.Plugin, error) {
	var loadedPlugins []*plugins.Plugin
	for _, p := range found {
		if _, exists := l.pluginRegistry.Plugin(ctx, p.Primary.JSONData.ID); exists {
			l.log.Warn(" Skipping plugin loading as it's a duplicate", "pluginID", ctx, p.Primary.JSONData.ID)
			continue
		}
		//  plugin signature skip

		//new plugin
		plugin, err := l.createPluginBase(p.Primary.JSONData, src.PluginClass(ctx), p.Primary.FS)
		if err != nil {
			l.log.Error("Could not create primary plugin base", "pluginID", p.Primary.JSONData.ID, "err", err)
			continue
		}
		loadedPlugins = append(loadedPlugins, plugin)

		for _, c := range p.Children {
			if _, exists := l.pluginRegistry.Plugin(ctx, c.JSONData.ID); exists {
				l.log.Warn("Skipping plugin loading as it's a duplicate", "pluginID", p.Primary.JSONData.ID)
				continue
			}

			cp, err := l.createPluginBase(c.JSONData, plugin.Class, c.FS)
			if err != nil {
				l.log.Error("Could not create child plugin base", "pluginID", p.Primary.JSONData.ID, "err", err)
				continue
			}

			plugin.Children = append(plugin.Children, cp)
			loadedPlugins = append(loadedPlugins, cp)
		}

	}
	//中间验证插件功能未实现
	//initialize plugins
	initializedPlugins := make([]*plugins.Plugin, 0)
	for _, p := range loadedPlugins {
		if err := l.pluginInitializer.Initialize(ctx, p); err != nil {
			l.log.Error("Could not initialize plugin", "pluginId", p.ID, "err", err)
			continue
		}
		initializedPlugins = append(initializedPlugins, p)
	}

	for _, p := range initializedPlugins {
		if err := l.load(ctx, p); err != nil {
			l.log.Error("could not start plugin", "pluginId", p.ID, err)
		}

		if !p.IsCorePlugin() {
			//增加指标
		}
	}
	return loadedPlugins, nil
}

// createPluginBase 生成插件对象
func (l *Loader) createPluginBase(pluginJson plugins.JSONData, class plugins.Class, file plugins.FS) (*plugins.Plugin, error) {
	plugin := &plugins.Plugin{
		JSONData: pluginJson,
		FS:       file,
		Class:    class,
	}
	plugin.SetLogger(log.New(fmt.Sprintf("plugin.%s", plugin.ID)))
	return plugin, nil
}

// Unload 取消加载插件
func (l *Loader) Unload(ctx context.Context, pluginID string) error {
	plugin, exists := l.pluginRegistry.Plugin(ctx, pluginID)
	if !exists {
		return plugins.ErrPluginNotInstalled
	}
	if !plugin.IsExternalPlugin() {
		return plugins.ErrUninstallCorePlugin
	}

	if err := l.unload(ctx, plugin); err != nil {
		return err
	}
	return nil
}
func (l *Loader) load(ctx context.Context, p *plugins.Plugin) error {
	if err := l.pluginRegistry.Add(ctx, p); err != nil {
		return err
	}
	if p.IsCorePlugin() {
		return nil
	}
	if !p.IsCorePlugin() {
		l.log.Info("Plugin registered", "pluginID", p.ID)
	}
	if p.IsExternalPlugin() {
		if err := l.pluginStorage.Register(ctx, p.ID, p.FS.Base()); err != nil {
			return err
		}
	}
	return l.processManager.Start(ctx, p.ID)
}
func (l *Loader) unload(ctx context.Context, p *plugins.Plugin) error {
	l.log.Debug("Stopping plugin process", "pluginId", p.ID)

	if err := l.processManager.Stop(ctx, p.ID); err != nil {
		return err
	}

	if err := l.pluginRegistry.Remove(ctx, p.ID); err != nil {
		return err
	}
	l.log.Debug("Plugin unregistered", "pluginId", p.ID)

	//if err := l.pluginStorage.Remove(ctx, p.ID); err != nil {
	//	return err
	//}
	return nil
}
